package com.fasteat.menu.util;

import com.fasteat.common.entity.GlobalException;
import com.fasteat.common.entity.StatusCode;
import com.fasteat.menu.annotation.Unchangeable;
import com.fasteat.menu.entity.*;
import com.fasteat.menu.mapper.*;
import io.swagger.annotations.ApiModelProperty;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

/**
 * @author run
 * @since 2021/1/20 20:06
 */
@Component
public class UpdateProxy {

    @Autowired
    private MaterialMapper materialMapper;

    @Autowired
    private MenuMapper menuMapper;

    @Autowired
    private ReportTypeMapper typeMapper;

    @Autowired
    private ReportMessageMapper messageMapper;

    private Object target;

    public UpdateProxy bind(Object target){
        this.target = target;
        return this;
    }

    public Object getProxyInstance(){
        return Proxy.newProxyInstance(target.getClass().getClassLoader()
                , getInterfaces(target.getClass()).toArray(new Class[0])
                , (obj,method,objects)->{
                    //获取修改的类型
                    Object change = objects[0];
                    Class<?> aClass = change.getClass();
                    Field[] fields;
                    Object origin = new Object();
                    if (aClass.equals(Menu.class)){
                        fields = Menu.class.getDeclaredFields();
                        origin = menuMapper.selectById(((Menu)change).getId());
                    }else if (aClass.equals(Material.class)){
                        fields = Material.class.getDeclaredFields();
                        origin = materialMapper.selectById(((Material)change).getId());
                    }else if (aClass.equals(ReportMessage.class)){
                        fields = ReportMessage.class.getDeclaredFields();
                        origin = messageMapper.selectById(((ReportMessage)change).getId());
                    }else {
                        fields = ReportType.class.getDeclaredFields();
                        origin = typeMapper.selectById(((ReportType)change).getId());
                    }
                    if (origin == null){
                        throw new GlobalException(StatusCode.ERROR,"所修改的对象不存在");
                    }
                    for (Field field : fields) {
                        //将private属性设置可访问
                        field.setAccessible(true);
                        if (field.isAnnotationPresent(Unchangeable.class)){
                            if (!field.get(change).equals(field.get(origin))){
                                throw new GlobalException(StatusCode.ERROR, getErrorMessage(field));
                            }
                        }
                    }
                    Object result = method.invoke(target,objects);
                    return result;
                });
    }

    //生成异常错误信息
    private static String getErrorMessage(Field field){
        String message = field.getAnnotation(Unchangeable.class).message();
        if (!message.isEmpty()){
            return message;
        }
        return field.getName() + (field.isAnnotationPresent(ApiModelProperty.class)
                ? ("（" + field.getAnnotation(ApiModelProperty.class).value() + "）") : "")
                + "为不可变属性，无法修改";
    }

    //获取类的所有接口，包括间接实现的
    private static List<Class<?>> getInterfaces(Class<?> implClass){
        if (implClass.equals(Object.class)){
            return Collections.emptyList();
        }
        List<Class<?>> classes = new ArrayList<>(Arrays.asList(implClass.getInterfaces()));
        classes.addAll(getInterfaces(implClass.getSuperclass()));
        return classes;
    }
}
